use super::TradeRequest;
use crate::model::kline::Kline;
use anyhow::anyhow;
use anyhow::Result;
use rhai::plugin::*;
use rhai::Dynamic;
use rhai::INT;
use std::collections::VecDeque;
use std::sync::Arc;
use std::sync::Mutex;
use tokio::io::stdout;
use tokio::io::AsyncWriteExt;
use tokio::sync::mpsc;

pub type SharedTradeContext = Arc<Mutex<TradeContext>>;

///
/// Singleton pattern implementation for TradeCommand
///
/// TradeContext is a singleton object,
///
/// which is updated by trade_bot , new klines arrive
///
/// and used to calculate buy/sell signal and place buy/sell trade_order
///
#[derive(Debug)]
pub struct TradeContext {
    /// TradeBot id
    pub trade_bot_id: String,
    /// Window klines, used to calculate buy/sell signal and place buy/sell trade_order
    pub window_klines: Vec<Kline>,

    pub trade_request_queue: TradeRequestQueue,
}

type TradeRequestQueue = Arc<Mutex<VecDeque<TradeRequest>>>;

impl TradeContext {
    pub fn new(trade_bot_id: String, klines: Vec<Kline>) -> Self {
        TradeContext {
            trade_bot_id,
            window_klines: klines,
            trade_request_queue: Arc::new(Mutex::new(VecDeque::new())),
        }
    }
}

// Plugin module for trade_api
/// Refer to [Singleton Command Object](https://rhai.rs/book/patterns/singleton.html)
#[export_module]
pub mod trade_api {
    use rhai::INT;
    #[rhai_fn(pure)]
    pub fn buy(context: &mut SharedTradeContext, amount: INT) {
        // println!("(trade_api::buy):  context={:?}", context);

        println!(
            "(trade_api::buy) trade_bot_id= {:?}, amount= {}",
            context.lock().unwrap().trade_bot_id,
            amount
        );

        let rs = queue_buy_request(context, amount);

        println!("(trade_api::buy) {:?}", rs);
    }

    #[rhai_fn(pure)]
    pub fn sell(context: &mut SharedTradeContext, amount: INT) {
        // println!("(trade_api::sell):  context={:?}", context);

        println!(
            "(trade_api::sell) trade_bot_id= {:?}, amount= {}",
            context.lock().unwrap().trade_bot_id,
            amount
        );

        let rs = queue_sell_request(context, amount);

        println!("(trade_api::sell) {:?}", rs);
    }

    /// Returns the list of Klines from the shared trade context.
    #[rhai_fn(pure)]
    pub fn klines(context: &mut SharedTradeContext) -> Dynamic {
        context.lock().unwrap().window_klines.clone().into()
    }
}

fn queue_buy_request(context: &mut SharedTradeContext, amount: INT) -> Result<()> {
    println!("(trade_api::send_buy_request) amount={:?}", amount);

    let locked_context = context
        .lock()
        .map_err(|e| anyhow!("Failed to lock shared_trade_context: {}", e))?;

    let current_kline = locked_context
        .window_klines
        .last()
        .ok_or_else(|| anyhow!("No klines found in the context"))?;

    if let Some(t) = &current_kline.id {
        let trade_request =
            TradeRequest::new_buy(&locked_context.trade_bot_id, t.id.to_raw(), amount);

        let mut queue = locked_context.trade_request_queue.lock().unwrap();
        queue.push_back(trade_request);
    }

    Ok(())
}

fn queue_sell_request(context: &mut SharedTradeContext, amount: INT) -> Result<()> {
    let locked_context = context
        .lock()
        .map_err(|e| anyhow!("Failed to lock shared_trade_context: {}", e))?;

    let current_kline = locked_context
        .window_klines
        .last()
        .ok_or_else(|| anyhow!("No klines found in the context"))?;

    if let Some(t) = &current_kline.id {
        let trade_request =
            TradeRequest::new_sell(&locked_context.trade_bot_id, t.id.to_raw(), amount);

        let mut queue = locked_context.trade_request_queue.lock().unwrap();
        queue.push_back(trade_request);
    }

    Ok(())
}
